package com.cherry.i18n.response;


import com.cherry.common.exception.enums.CommonErrorCode;
import com.cherry.common.model.Result;
import com.cherry.i18n.config.properties.I18nProperties;
import com.cherry.i18n.enums.I18nCodeType;
import com.cherry.i18n.enums.MessageArgFormatType;
import com.cherry.i18n.service.I18nService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

/**
 * 利用 ResponseBodyAdvice 对返回结果进行国际化处理
 *
 * @author sinbad.cheng
 * @since 2021-11-11
 */
@RestControllerAdvice
@ConditionalOnBean(I18nProperties.class)
public class I18nResponseAdvice implements ResponseBodyAdvice<Object> {

    private final I18nService i18nService;

    private final I18nProperties i18nProperties;

    public I18nResponseAdvice(I18nService i18nService, I18nProperties i18nProperties) {
        this.i18nService = i18nService;
        this.i18nProperties = i18nProperties;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (!(body instanceof Result)) {
            return body;
        }

        Result<?> result = (Result<?>) body;
        return handleI18n(result);
    }

    /**
     * ApiResult 国际化处理
     *
     * @param result 返回体
     * @return {@link Result}
     */
    private Result<?> handleI18n(Result<?> result) {
        // ApiResult返回结果
        int resultCode = result.getCode();
        String resultMsg = result.getMsg();
        Object[] resultArgs = result.getArgs();

        // 消息编号类型
        I18nCodeType i18nCodeType = i18nProperties.getCodeType();
        // 消息参数格式化类型：String.format or MessageFormat
        MessageArgFormatType messageArgFormatType = Optional.ofNullable(i18nProperties
                        .getMessageArgFormat()).orElse(MessageArgFormatType.STRING_FORMAT);
        // 是否使用Result.code作为messageCode
        boolean isUseResultCode = Objects.equals(i18nCodeType, I18nCodeType.RESULT_CODE);
        // 消息编号
        String messageCode = isUseResultCode ? I18nProperties.RESULT_CODE_PREFIX + resultCode : resultMsg;
        // 上下文国际化区域
        Locale locale = LocaleContextHolder.getLocale();

        // 这里针对通用异常枚举做特殊处理，因为validation或其他参数校验等都用了这个code
        if (isUseResultCode) {
            CommonErrorCode commonErrorCode = CommonErrorCode.get(resultCode);
            if (commonErrorCode != null && Objects.equals(resultMsg, commonErrorCode.getMsg())) {
                messageCode = resultMsg;
            }
        }

        // 不做参数处理，直接返回国际化消息
        if (ArrayUtils.isEmpty(resultArgs)) {
            return result.msg(i18nService.getMessage(messageCode, locale, resultMsg));
        }
        // 置空参数，让 ResultResponseAdvice 不做参数处理
        result.setArgs(null);

        // STRING_FORMAT，参数默认走String.format格式化（放第一位）
        if (Objects.equals(messageArgFormatType, MessageArgFormatType.STRING_FORMAT)) {
            String message = i18nService.getMessage(messageCode, locale, resultMsg);
            return result.msg(String.format(message, resultArgs));
        }

        // ALL，参数先走一遍MessageFormat处理，再走String.format处理
        if (Objects.equals(messageArgFormatType, MessageArgFormatType.ALL)) {
            String message = i18nService.getMessage(messageCode, locale, resultMsg, resultArgs);
            return result.msg(String.format(message, resultArgs));
        }

        // 国际化消息处理
        return result.msg(i18nService.getMessage(messageCode, locale, resultMsg, resultArgs));
    }

}